#include <iostream>
#include <exception>
#include <vector>
#include <string>
#include <cstring>
// #include <stdexcept>

// reference: http://www.cplusplus.com/doc/tutorial/exceptions/
int test_exception1()
{
    try
    {
        throw 20;
    }
    catch (int e)
    {
        std::cout << "An exception occurred. Exception Nr. " << e << '\n';
    }

    return 0;
}

class myexception : public std::exception
{
public:
    myexception(const char *err)
    {
        perror = new char[strlen(err) + 1];
        strcpy(perror, err);
    }
    myexception(const myexception &ex)  // 拷贝构造
    {
        this->perror = new char[strlen(ex.perror) + 1];
        strcpy(this->perror, ex.perror);
    }
    myexception &operator=(const myexception &ex)  // =重载
    {
        if (this->perror != nullptr)
        {
            delete[] perror;
            this->perror = nullptr;
        }
        this->perror = new char[strlen(ex.perror) + 1];
        strcpy(this->perror, ex.perror);

        return *this;
    }
    ~myexception()
    {
        if (perror != nullptr)
        {
            delete[] perror;
            perror = nullptr;
        }
    }

    virtual const char *what() const throw()
    {
        return perror;
    }

public:
    char *perror;
};

int test_exception2()
{
    try
    {
        // throw std::runtime_error("123");    // myexception
        // throw std::out_of_range("1234");    // myexception
        throw myexception("myexception is throwed 0703");
    }
    catch (std::exception &e)
    { // catches exception objects by reference (notice the ampersand & after the type)
        std::cout << e.what() << '\n';
    }
    std::cout << "go on ..." << std::endl;

    return 0;
}

int test_exception3()
{
    /*
	exception		    description
	bad_alloc		    thrown by new on allocation failure
	bad_cast		    thrown by dynamic_cast when it fails in a dynamic cast
	bad_exception		thrown by certain dynamic exception specifiers
	bad_typeid		    thrown by typeid
	bad_function_call	thrown by empty function objects
	bad_weak_ptr		thrown by shared_ptr when passed a bad weak_ptr
*/
    try
    {
        int *myarray = new int[1000];
    }
    catch (std::exception &e)
    { // Takes a reference to an 'exception' object
        std::cout << "Standard exception: " << e.what() << std::endl;
    }

    return 0;
}

// reference: http://en.cppreference.com/w/cpp/language/try_catch
int test_exception4()
{
    try
    {
        std::cout << "Throwing an integer exception...\n";
        throw 42;
    }
    catch (int i)
    {
        std::cout << " the integer exception was caught, with value: " << i << '\n';
    }

    try
    {
        std::cout << "Creating a vector of size 5... \n";
        std::vector<int> v(5);
        std::cout << "Accessing the 11th element of the vector...\n";
        std::cout << v.at(10);           // vector::at() throws std::out_of_range
        std::cout << "111" << std::endl; // last line throwed, this line doesn't run
    }
    catch (const std::exception &e)
    { // caught by reference to base
        std::cout << " a standard exception was caught, with message '"
                  << e.what() << "'\n";
    }

    std::cout << "111" << std::endl; // this line is after the last catch, so this line can run whenever try...catch run or not

    return 0;
}

// reference: https://www.tutorialspoint.com/cplusplus/cpp_exceptions_handling.htm
static int division(int a, int b)
{
    if (b == 0)
    {
        throw "Division by zero condition!";
    }
    return (a / b);
}

int test_exception5()
{
    int x{50}, y{0}, z{0};

    try
    {
        z = division(x, y);
        std::cout << z << std::endl;
        std::cout << "123intry" << std::endl;
    }
    catch (const char *msg)
    {
        std::cerr << msg << std::endl;
    }

    std::cout << "123outtry" << std::endl;
    return 0;
}

struct MyException : public std::exception
{
    const char *what() const throw()
    {
        return "C++ Exception";
    }
};

int test_exception6()
{
    try
    {
        // throw std::exception();
        throw MyException();
    }
    catch (MyException &e)
    {
        std::cout << "MyException caught" << std::endl;
        std::cout << e.what() << std::endl;
    }
    catch (std::exception &e)
    {
        //Other errors
        std::cout << e.what() << std::endl;
    }

    return 0;
}

int test_exception7()
{
    std::string str1 = " lux ";
    try
    {
        char *str = nullptr;
        str = new char[10]; // new(std::nothrow)
        if (str == nullptr)
            throw "Allocation failure";
        for (int n = 0; n <= 100; n++)
        {
            if (n > 9)
                // throw std::exception();
                throw std::runtime_error("Exception: out_of_range:" + str1);
            str[n] = 'z';
        }
    }
    catch (int i)
    {
        std::cout << "Exception: ";
        std::cout << "index " << i << " is out of range" << std::endl;
    }
    catch (const char *str)
    {
        std::cout << "Exception: " << str << std::endl;
    }
    catch (const std::exception &e)
    {
        std::cout << e.what() << ": a b c d !" << std::endl;
    }

    std::cout << "*** go on after catch ***" << std::endl;

    return 0;
}

int main(int argc, char **argv)
{
    test_exception2();
    return 1;
}